#include "command.h"

// 下面的函数由command_ls调用
void str_to_lower(char* str);// 将大写转换为小写
static int cmp_string(const void *p1, const void *p2);// 传给qsort的比较函数
void set_time(time_t mtime,char* time_str);// 将时间戳转换为字符串
void set_type_mode(mode_t mode,char *tm_str);// 将文件类型和权限转换为字符串

// 
int command_ls(const char *root_dir,const char *current_dir,const char *dest_dir, commandResult_t *commandResult){

    if(root_dir==NULL || current_dir==NULL || dest_dir==NULL || commandResult==NULL){
        printf("参数不能为NULL！\n");
        return -1;
    }

    int total_len = strlen(root_dir)+strlen(current_dir)+strlen(dest_dir)+3;
    char *absolute_path = (char *)calloc(total_len,sizeof(char)); 
    snprintf(absolute_path,total_len,"%s/%s/%s",root_dir,current_dir,dest_dir);

    DIR* dirp = opendir(absolute_path);  
    if(dirp==NULL){
        printf("opendir failed\n");
        return -1;
    }
    struct dirent* pdirent;
    int count = 0;

    // 计算目录项的数量
    while((pdirent = readdir(dirp))!=NULL){
        count++;
    }

    // 倒带目录流指针
    rewinddir(dirp);

    // 申请一个结构体指针数组，要把pdirent里的信息存进结构体指针数组里
    struct dirent** dir_arr = (struct dirent**)malloc(sizeof(struct dirent*) * count);

    // 将目录项的结构体对象指针存入数组dir_arr中
    int idx = 0;
    while((pdirent = readdir(dirp))!=NULL){
        dir_arr[idx] = pdirent;
        idx++;
    }

    // 按照文件名字的字典序排序
    qsort(dir_arr, count, sizeof(struct dirent *), cmp_string);

    // 为commandResult的result_ls申请内存空间，调用方需要释放该内存空间
    commandResult->result_ls = (char **)malloc(sizeof(char *)*count);
    commandResult->ls_linenum = count;

    for(int i = 0;i < count; ++i){

        struct stat stat_buf;
        // 从stat结构体里获取文件名
        // 由于不能使用chdir，所以需要拼接出当前的目录
        char fullpath[4096];
        snprintf(fullpath, sizeof(fullpath), "%s/%s", absolute_path, dir_arr[i]->d_name);
        int ret = stat(fullpath, &stat_buf);
        if(ret==-1){
            printf("stat in command_ls failed\n");
            return -1;
        }

        // 将stat结构体中的st_mode转换成权限以及文件类型字符串
        char tm_str[1024] = {0};
        set_type_mode(stat_buf.st_mode, tm_str);

        // 获取用户名和组名
        char *username = getpwuid(stat_buf.st_uid)->pw_name;
        char *gname = getgrgid(stat_buf.st_gid)->gr_name;

        // 将时间戳转换为时间字符串
        char time_str[1024] = {0};
        set_time(stat_buf.st_mtim.tv_sec,time_str);

        // 将打印的内容最终存储到结构体的result_ls变量中
        char line[4096];
        snprintf(line,sizeof(line),"%s %2lu %s %s %6lu %s %s\n",
                 tm_str,
                 stat_buf.st_nlink,
                 username,
                 gname,
                 stat_buf.st_size,
                 time_str,
                 dir_arr[i]->d_name  );
        commandResult->result_ls[i] = strdup(line);
    }

    free(dir_arr);
    closedir(dirp);
    free(absolute_path);
    //freeCommandResult(commandResult);
    return 0;
}

int command_pwd(const char *current_dir, commandResult_t *commandResult){
    if(current_dir == NULL || commandResult==NULL){
        printf("参数不能为NULL！\n");
        return -1;
    }
    initCommandResult(commandResult);
    int path_len = strlen(current_dir);
    commandResult->result = (char *)malloc(sizeof(char)*path_len+2);// \n多占一个字节，\0多占一个字节
    if(commandResult->result==NULL){
        printf("pwd malloc failed\n");
        return -1;
    }
    snprintf(commandResult->result,path_len+2,"%s\n",current_dir);
    //freeCommandResult(commandResult);
    return 0;
}

// 从当前工作目录curr_path，新建一个名为dir_name的目录
int command_mkdir(const char *root_dir, const char *current_dir, const char *dest_dir, commandResult_t *commandResult){


    if(root_dir==NULL || current_dir==NULL || dest_dir==NULL || commandResult==NULL){
        printf("参数不能为NULL！\n");
        return -1;
    }
    
    int total_len = strlen(root_dir)+strlen(current_dir)+2;
    char *absolute_path = (char *)calloc(total_len,sizeof(char)); 
    snprintf(absolute_path,total_len,"%s/%s",root_dir,current_dir);

    if(access(absolute_path,F_OK)!=0){
        printf("不是一个合法路径！\n");
        commandResult->ret = -1;
        return -1;
    }

    total_len = total_len+strlen(dest_dir)+1;
    absolute_path = (char *)realloc(absolute_path,total_len);
    if(absolute_path==NULL){
        printf("command_mkdir拼接路径分配内存失败！\n");
        return -1;
    }
    snprintf(absolute_path,total_len,"%s/%s/%s",root_dir,current_dir,dest_dir);

    int ret = mkdir(absolute_path,0775);
    if(ret!=0){
        printf("mkdir failed\n");
        return -1;
    } 
    free(absolute_path);
    //freeCommandResult(commandResult);
    return 0;
}


int command_rmdir(const char *root_dir, const char *current_dir, const char *dest_dir, commandResult_t *commandResult){

    if(root_dir==NULL || current_dir==NULL || dest_dir==NULL || commandResult==NULL){
        printf("参数不能为NULL！\n");
        return -1;
    }

    int total_len = strlen(root_dir)+strlen(current_dir)+strlen(dest_dir)+3;
    char *absolute_path = (char *)calloc(total_len,sizeof(char)); 
    snprintf(absolute_path,total_len,"%s/%s/%s",root_dir,current_dir,dest_dir);

    if(access(absolute_path,F_OK)!=0){
        printf("传入的路径不存在！\n");
        return -1;
    }
    int ret = rmdir(absolute_path);
    if(ret!=0){
        printf("rmdir failed,目录可能非空、不存在或没有删除权限\n");
        return -1;
    } 
    free(absolute_path);
    //freeCommandResult(commandResult);
    return 0;
}

// 初始化commmandResult结构体
int initCommandResult(commandResult_t *commandResult){
    commandResult->result = NULL;
    commandResult->result_ls = NULL;
    commandResult->ret = 0;
    commandResult->ls_linenum = 0;
    return 0;
}

// 释放commandResult结构体申请的内存
int freeCommandResult(commandResult_t *commandResult){
    if(commandResult == NULL){
        printf("传入的commandResult为NULL，无法进行资源释放！\n");
        return -1; 
    }
    printf("before free result\n");
    // 释放result
    if(commandResult->result!=NULL){
        free(commandResult->result);
        commandResult->result = NULL;
    }
    printf("after free result\n");

    // 释放result_ls
    if(commandResult->result_ls!=NULL){
        for(int i = 0; i < commandResult->ls_linenum;++i){
            if(commandResult->result_ls[i]!=NULL){
                free(commandResult->result_ls[i]);
                commandResult->result_ls[i] = NULL;
            }
        }
        free(commandResult->result_ls);
        commandResult->result_ls = NULL;
    }

    printf("after free result_ls\n");
    commandResult->ls_linenum = 0;

    return 0;
}



void str_to_lower(char* str){
    while(*str){
        *str = tolower(*str);
        str++;
    }
}

static int cmp_string(const void *p1, const void *p2)
{
    struct dirent* dir_a = *(struct dirent**)p1;
    struct dirent* dir_b = *(struct dirent**)p2;


    char filename1[256];
    char filename2[256];

    strncpy(filename1,dir_a->d_name,sizeof(filename1)-1);
    strncpy(filename2,dir_b->d_name,sizeof(filename2)-1);
    filename1[sizeof(filename1)-1]='\0';
    filename2[sizeof(filename2)-1]='\0';

    str_to_lower(filename1);
    str_to_lower(filename2);
    return strcmp(filename1,filename2);
}

void set_time(time_t mtime,char* time_str){
    const char month[][10] = {
        "1月", "2月", "3月", "4月", "5月", "6月",
        "7月", "8月", "9月", "10月", "11月", "12月"
    };
    struct tm *st_tm = localtime(&mtime);
    sprintf(time_str,"%s %2d %02d:%02d",
            month[st_tm->tm_mon],
            st_tm->tm_mday,
            st_tm->tm_hour,
            st_tm->tm_min);
}

void set_type_mode(mode_t mode,char *tm_str){
    switch (mode & S_IFMT) {
    case S_IFBLK:  tm_str[0]='b';            break;
    case S_IFCHR:  tm_str[0]='c';            break;
    case S_IFDIR:  tm_str[0]='d';            break;
    case S_IFIFO:  tm_str[0]='p';            break;
    case S_IFLNK:  tm_str[0]='l';            break;
    case S_IFREG:  tm_str[0]='-';            break;
    case S_IFSOCK: tm_str[0]='s';            break;
    default:       tm_str[0]='?';            break;
    }

    tm_str[1]=(mode&0400)?'r':'-';
    tm_str[2]=(mode&0200)?'w':'-';
    tm_str[3]=(mode&0100)?'x':'-';
    tm_str[4]=(mode&0040)?'r':'-';
    tm_str[5]=(mode&0020)?'w':'-';
    tm_str[6]=(mode&0010)?'x':'-';
    tm_str[7]=(mode&0004)?'r':'-';
    tm_str[8]=(mode&0002)?'w':'-';
    tm_str[9]=(mode&0001)?'x':'-';
    tm_str[10]='\0';

}

